// DllImport - A C#-like DLL Wrapper
// written by oldrev (wstring#gmail.com)
// License: BSD

module windump.dll;

import std.typetuple;
import std.c.windows.windows;
import std.traits;
import std.string;

import windump.common;
import windump.utf;

extern(Windows)
{
	HMODULE LoadLibraryW(LPCWSTR libPath);
}


final class Module : IDisposable
{
    private HMODULE m_handle = null; 
    private string  m_name;

    public this(string path)
    {
        m_name = path;
        m_handle = LoadLibraryW(toUtf16z(path));    
        if(m_handle is null)
            throw new Exception("Failed to LoadLibrary: " ~ path);
    }

    public ~this()
    {
        close();
    }

    public void close()
    {
        if(m_handle != null)
        {
            FreeLibrary(m_handle);
            m_handle = null;
        }
    }

    public void dispose()
    {
        close();
    }

    public HMODULE handle()
    {
        return m_handle;
    }

    public FuncType findSymbol(FuncType)(string name)
    in
    {
        assert(!(name is null));
        assert(name.length > 0);
    }
    body
    {
        return cast(FuncType)(GetProcAddress(m_handle, toStringz(name)));
    }

    public FuncType getSymbol(FuncType)(string name)
    in
    {
        assert(!(name is null));
        assert(name.length > 0);
    } 
    body
    {
        FuncType f = findSymbol!(FuncType)(name);
        if(f is null)
            throw new Exception("Failed to findSymbol");
        return f;
    }

    public string name()
    {
        return m_name;
    }

}


private static class ModuleManager : IDisposable
{
	private static Module	[string]	m_modules;

	private this()
	{
	}

    public void close()
    {
        if((m_modules is null) || (m_modules.length == 0)) return;

		foreach(h; m_modules)
		{
			h.close();
		}
    }

    public void dispose()
    {
        close();
    }

	static public ~this()
	{
        close();
	}

	private static Module registerModule(string name)
	{
		string lname = tolower(name);
        Module h = new Module(lname);
		if(h is null)
			throw new Exception("Failed to load DLL: " ~ name);
		m_modules[lname] = h;
		return h;
	}

	public static HMODULE getHandle(string name)
	{
		return m_modules[name].handle;
	}

	public static ProcType getSymbol(ProcType)(string moduleName, string procName)
	{
        Module m = moduleName in m_modules ? m_modules[moduleName] : registerModule(moduleName);
		assert(m !is null);

        ProcType proc = m.getSymbol!(ProcType)(procName);

		if(proc is null)
			throw new Exception("Cannot to get the address of " ~ procName);
		return proc;
	}
}

struct DllImport(string ModuleName, string ProcName, FT)
{
   	extern(Windows) alias ReturnType!(FT) 
        function(ParameterTypeTuple!(FT))     FunctionType;
	alias DllImport!(ModuleName, ProcName, FT) SelfType;

	private FunctionType m_funcPtr = null;		

	public ReturnType!(FunctionType) opCall(ParameterTypeTuple!(FunctionType) args)
	{
		if(m_funcPtr is null)
			m_funcPtr = ModuleManager.getSymbol!(FunctionType)(ModuleName, ProcName);		
		return m_funcPtr(args);
	}
}

 
/*
void main()
{
	DllImport!("user32.dll", "MessageBoxA", 
			int function(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)) MessageBox;

	MessageBox(null, "Text", "Title", MB_OK);
}
*/
